#include "MShape.h"

std::ostream& operator<<(std::ostream& os , const gp_Pnt& pnt)
{
	os << pnt.X() << ", " << pnt.Y() << ", " << pnt.Z();
	return os;
}

Handle(Geom_BSplineCurve) createBSplineCurve(std::vector<gp_Pnt>& pnts, int mode)
{
	int pntSize = pnts.size();
	Handle(TColgp_HArray1OfPnt) pntsArr = new TColgp_HArray1OfPnt(1 , pntSize);
	for (int i = 0; i < pntSize; ++i)
	{
		TopoDS_Vertex aVertex = BRepBuilderAPI_MakeVertex(pnts[i]);
		pntsArr->SetValue(i + 1 , pnts[i]);
	}

	Handle(Geom_BSplineCurve) curve;
	if (mode == 0)
	{
		GeomAPI_Interpolate Interp(pntsArr , false , Precision::Confusion());
		Interp.Perform();
		curve = Interp.Curve();
	}
	else if (mode == 1)
	{
		GeomAPI_PointsToBSpline Approx(pntsArr->Array1());
		curve = Approx.Curve();
	}

	return curve;
}

TopoDS_Shape correctionProfile(TopoDS_Shape& aProfile , gp_Pnt fromPnt , Handle(Geom_Curve) CPath , double toParam)
{
	TopoDS_Face aFace;
	if (aProfile.ShapeType() == TopAbs_FACE)
	{
		aFace = TopoDS::Face(aProfile);
	}
	else if (aProfile.ShapeType() == TopAbs_WIRE)
	{
		aFace = BRepBuilderAPI_MakeFace(TopoDS::Wire(aProfile));
	}

	gp_Pnt toPoint;
	gp_Vec derivative;
	CPath->D1(toParam , toPoint , derivative);
	//Handle(Geom_Surface) surface = BRep_Tool::Surface(aFace);
	BRepAdaptor_Surface surface(aFace);
	gp_Vec direc = surface.Plane().Axis().Direction();

	gp_Trsf T;
	T.SetTranslation(gp_Vec(fromPnt , gp_Pnt(0 , 0 , 0)));
	BRepBuilderAPI_Transform theTrsf(aProfile , T);

	gp_Quaternion quater(direc , derivative);
	gp_Vec vecTranslate = gp_Vec(gp_Pnt(0 , 0 , 0) , toPoint);
	gp_Trsf TRet;
	TRet.SetTransformation(quater , vecTranslate);
	BRepBuilderAPI_Transform theTrsfRet(theTrsf.Shape() , TRet);

	return theTrsfRet.Shape();
}

TopoDS_Wire convertFaceToWire(const TopoDS_Shape& aShape)
{
	BRepBuilderAPI_MakeWire wireBuilder;
	for (TopExp_Explorer anEdgeExp(aShape , TopAbs_EDGE); anEdgeExp.More(); anEdgeExp.Next())
	{
		const TopoDS_Edge& anEdge = TopoDS::Edge(anEdgeExp.Current());
		wireBuilder.Add(anEdge);
	}
	return wireBuilder.Wire();
}

MSAbstract::MSAbstract(const QString& n) :
	myName(n)
{
	myType = MSType::MSType_NONE;
}

gp_Pnt MSAbstract::transformPoint(gp_Pnt& p , gp_Vec vecTranslate)
{
	gp_Pnt ret;
	gp_Trsf T;
	T.SetTranslation(vecTranslate);
	ret = p.Transformed(T);

	return ret;
}

TopoDS_Shape MSAbstract::transformShape(TopoDS_Shape& aShape , gp_Quaternion quater , gp_Vec vecTranslate)
{
	gp_Trsf T;
	T.SetTransformation(quater , vecTranslate);
	BRepBuilderAPI_Transform theTrsf(T);
	theTrsf.Perform(aShape);

	return theTrsf.Shape();
}

MSLinearRect::MSLinearRect(const std::vector<gp_Pnt>& points , double width , double height , const QString& n) :
	MSAbstract(n) ,
	myPoints(points) ,
	myWidth(width) ,
	myHeight(height)
{
	setType(MSType::MSType_LINEAR_RECT);
	compute();
}

TopoDS_Shape MSLinearRect::createProfile()
{
	gp_Pnt aStart = myPoints[0];
	gp_Pnt aEnd = myPoints[1];

	gp_Vec vec = gp_Vec(aStart , aEnd);
	gp_Vec vecX = vec.Crossed(gp_Vec(0 , 0 , 1));
	gp_Ax3 ax3 = gp_Ax3(aStart , vec , vecX);
	Handle(Geom_Surface) surf = new Geom_Plane(ax3);

	gp_Pnt2d pnt2d1 = gp_Pnt2d(-myWidth / 2 , myHeight / 2);
	gp_Pnt2d pnt2d2 = gp_Pnt2d(myWidth / 2 , myHeight / 2);
	gp_Pnt2d pnt2d3 = gp_Pnt2d(myWidth / 2 , -myHeight / 2);
	gp_Pnt2d pnt2d4 = gp_Pnt2d(-myWidth / 2 , -myHeight / 2);
	Handle(Geom2d_Line) geom2dLin1 = GCE2d_MakeLine(pnt2d1 , pnt2d2).Value();
	Handle(Geom2d_Line) geom2dLin2 = GCE2d_MakeLine(pnt2d2 , pnt2d3).Value();
	Handle(Geom2d_Line) geom2dLin3 = GCE2d_MakeLine(pnt2d3 , pnt2d4).Value();
	Handle(Geom2d_Line) geom2dLin4 = GCE2d_MakeLine(pnt2d4 , pnt2d1).Value();

	BRepBuilderAPI_MakeWire MW;
	MW.Add(BRepBuilderAPI_MakeEdge(geom2dLin1 , surf , 0 , myWidth));
	MW.Add(BRepBuilderAPI_MakeEdge(geom2dLin2 , surf , 0 , myHeight));
	MW.Add(BRepBuilderAPI_MakeEdge(geom2dLin3 , surf , 0 , myWidth));
	MW.Add(BRepBuilderAPI_MakeEdge(geom2dLin4 , surf , 0 , myHeight));
	TopoDS_Shape aProfile = MW;
	BRepLib::BuildCurves3d(aProfile);

	return aProfile;
}

void MSLinearRect::updateCompute(const std::vector<gp_Pnt>& pointsNew)
{
	myPoints = pointsNew;
	compute();
}

void MSLinearRect::compute()
{
	gp_Pnt aStart = myPoints[0];
	gp_Pnt aEnd = myPoints[1];

	TopoDS_Edge aEdgePath = BRepBuilderAPI_MakeEdge(aStart , aEnd);
	myPath = BRepBuilderAPI_MakeWire(aEdgePath);
	myProfile = createProfile();
	myShape = BRepOffsetAPI_MakePipe(myPath , myProfile);
}

MSPipeFitting::MSPipeFitting(std::shared_ptr< MSLinearRect> l , std::shared_ptr< MSLinearRect> r , const QString& n) :
	MSAbstract(n) ,
	lhs(l) ,
	rhs(r)
{
	std::vector<gp_Pnt> pntsLhs = l->getPoints();
	std::vector<gp_Pnt> pntsRhs = r->getPoints();
	myPoints = { pntsLhs[0] ,pntsLhs[1] ,pntsRhs[0], pntsRhs[1] };

	setType(MSType::MSType_PIPEFITING);
	compute();
}

TopoDS_Shape MSPipeFitting::createProfile()
{
	return TopoDS_Shape();
}

void MSPipeFitting::setAdjacent(const std::vector<gp_Pnt>& pointsLhs , const std::vector<gp_Pnt>& pointsRhs)
{
	lhs->updateCompute(pointsLhs);
	rhs->updateCompute(pointsRhs);
}

void MSPipeFitting::restoreAdjacent()
{
	std::vector<gp_Pnt> pntsLhs = {myPoints[0],myPoints[1] };
	std::vector<gp_Pnt> pntsRhs = { myPoints[2],myPoints[3] };
	lhs->updateCompute(pntsLhs);
	rhs->updateCompute(pntsRhs);
}

void MSPipeFitting::compute()
{
	gp_Pnt aFirst = myPoints[0];
	gp_Pnt aSecond = myPoints[1];
	gp_Pnt aThird = myPoints[2];
	gp_Pnt aFourth = myPoints[3];

	gp_Vec vecFirst = gp_Vec(aFirst , aSecond);
	gp_Vec vecSecond = gp_Vec(aThird , aFourth);

	double lhsWidth = lhs->getWidth();
	double rhsWidth = rhs->getWidth();
	double maxWidth = lhsWidth > rhsWidth ? lhsWidth : rhsWidth;
	double length = maxWidth / 2;

	double scalar = length / vecFirst.Magnitude();
	gp_Vec tanslate = vecFirst * scalar;
	gp_Pnt aSecondNew = transformPoint(aSecond , tanslate.Reversed());

	double scalar1 = length / vecSecond.Magnitude();
	gp_Vec tanslate1 = vecSecond * scalar1;
	gp_Pnt aThirdNew = transformPoint(aThird , tanslate1);
	setAdjacent({ aFirst ,aSecondNew } , { aThirdNew, aFourth });

	Handle(TColgp_HArray1OfPnt) points = new TColgp_HArray1OfPnt(1 , 2);
	points->SetValue(1 , aSecondNew);
	points->SetValue(2 , aThirdNew);
	GeomAPI_Interpolate interpolator(points , false , Precision::Confusion());
	interpolator.Load(vecFirst , vecSecond);
	interpolator.Perform();
	Handle(Geom_BSplineCurve) geomPath = interpolator.Curve();
	TopoDS_Edge aEdgePath = BRepBuilderAPI_MakeEdge(geomPath , geomPath->FirstParameter() , geomPath->LastParameter());
	myPath = BRepBuilderAPI_MakeWire(aEdgePath);

	TopoDS_Shape aProfile = lhs->createProfile();
	TopoDS_Shape aCorrection = correctionProfile(aProfile , aFirst , geomPath , geomPath->FirstParameter());
	TopoDS_Shape aProfile1 = rhs->createProfile();

	BRepOffsetAPI_MakePipeShell PipeShell(myPath);
	PipeShell.Add(aCorrection , false , false);
	PipeShell.Add(aProfile1 , false , false);

	myShape = PipeShell.Shape();
}